import { Selection, BaseType } from 'd3-selection';
import L, { latLng, LatLng, Point } from 'leaflet';
import { FeatureInstance, FeatureOption } from '../types';
import Feature from './feature';
import { line } from 'd3-shape';
import Vertex from './vertex';
import Line from './line';
import { pointInSegments } from '../geo/math';
import { deepClone } from '../util/util';
import { getAnotation } from '../util/util';

/**
 * 面路径需要绑定的数据类型
 */
type PathData = {
    className: string;
    latLngs: LatLng[][];
    nodeIds: string[][];
};

class Polygon extends Feature {
    /**
     * 正在绘制的标识
     */
    drawing: boolean = false;
    // @ts-ignore
    data: PathData[] = [];
    classNames: string[] = ['shadow', 'stroke'];
    /**
     * 绘制过程中不断移动的虚线
     */
    dashedLatlngs: LatLng[][] = []; // 末段虚线，拖动时可能会有多段虚线，所以是二维数组
    dashedVertex?: Vertex;
    relateNodeIds: string[][] = [[]]; // 按照顶点顺序排列

    constructor(id: string, featureOption: FeatureOption, latLngs: LatLng[][], style: any, properties?: any) {
        super(id, featureOption, latLngs, 'polygon', properties);

        if (latLngs.length === 1 && latLngs[0].length === 1) {
            const firstLatLng = latLngs[0][0];
            this.dashedLatlngs = [[firstLatLng, firstLatLng, firstLatLng]];
        }
    }

    initData() {
        const { clipPath } = this.featureOption;

        let latLngs = clipPath(this.latLngs);
        if (!latLngs || latLngs.length === 0) {
            // 裁切后没有可显示区域
            this.data = [];
        } else {
            // clipNodeIds：判断dragIndexs使用，第一次加载，relateNodeIds不存在，clipNodeIds全是undefined
            const clipNodeIds: string[][] = [];
            // 如果要素在视野边界被裁剪，实际绘制经纬度个数会变，对应的顶点个数也要变，不然拖动时，顶点和经纬度的对应关系会乱
            latLngs.forEach((latLngsItem, sequence) => {
                if (!clipNodeIds[sequence]) {
                    clipNodeIds[sequence] = [];
                }
                latLngsItem.forEach((latLng) => {
                    const existIndex = this.latLngs[sequence].findIndex((item) => latLng.equals(item));

                    if (existIndex !== -1) {
                        clipNodeIds[sequence].push(
                            this.relateNodeIds[sequence] ? this.relateNodeIds[sequence][existIndex] : ''
                        );
                    } else {
                        clipNodeIds[sequence].push('');
                    }
                });
            });

            this.data = this.classNames.map((className) => {
                return {
                    className,
                    latLngs,
                    nodeIds: clipNodeIds,
                };
            });
        }
    }

    add(groupSelection: Selection<SVGGElement | BaseType, Feature, BaseType, string>): Feature {
        this.group = groupSelection;
        this.redraw();
        this.addEventListener();
        return this;
    }

    redraw() {
        this.initData();
        this.group.selectChildren('path.polygon').remove();

        if (this.data.length > 0) {
            this.group
                .selectChildren('path.polygon')
                .data(this.data.flat())
                .enter()
                .append('path')
                .attr('class', (d) => {
                    return this.type + ' ' + d.className;
                })
                .call(this.drawPath.bind(this));
        }

        return this;
    }

    drawPath(selection: Selection<SVGPathElement, PathData, BaseType, Feature>) {
        const mode = this.featureOption.mode;

        selection.attr('d', (d) => {
            const { className, latLngs, nodeIds } = d;
            const paths = latLngs.map((latLngItem, sequence) => {
                const dragIndexs = nodeIds[sequence]
                    .map((id, index) => {
                        return mode.getType() === 'drag' && id === mode.selectedFeature?.id ? index : undefined;
                    })
                    .filter((item) => item !== undefined);

                const pixes = latLngItem.map((latLng) => {
                    return this.featureOption.transform.project(latLng);
                });

                return line().defined((d, i, data) => {
                    if (dragIndexs.length === 0 || className === 'fill') {
                        return true;
                    } else {
                        return !dragIndexs.includes(i);
                    }
                })(pixes);
            });
            return paths.join(' ');
        });
    }
    /**
     * 虚线
     * @returns
     */
    redrawDashedLine() {
        this.group.select('path.dashed-line').remove();
        this.dashedLatlngs.forEach((latLngs: LatLng[]) => {
            if (latLngs.length == 0) {
                return;
            }

            // 首尾是同一个点，则去掉第一个点，防止虚线重叠，发生闪烁
            let dashedLatlngs = latLngs;
            if (latLngs[0].lat == latLngs[2].lat && latLngs[0].lng == latLngs[2].lng) {
                dashedLatlngs = latLngs.slice(-2);
            }

            const pixes = dashedLatlngs.map((latLng) => {
                return this.featureOption.transform.project(latLng);
            });
            const d = line()(pixes);

            this.group
                .append('path')
                .attr('class', () => {
                    return this.type + ' dashed-line';
                })
                .attr('d', d);
        });
    }

    addLatLng(latLng: LatLng, drawVertex: boolean = true) {
        const sequence = this.latLngs.length - 1;

        this.latLngs[sequence].push(latLng);

        this.redraw();

        this.dashedLatlngs[0] = [latLng, latLng, this.latLngs[sequence][0]];
        this.redrawDashedLine();

        // 绘制完成 不用添加顶点
        if (drawVertex) {
            const vertexId = this.featureOption.addVertex!(latLng, [this], { className: 'vertex', size: 4 });
            this.relateNodeIds[sequence].push(vertexId);
        }
    }

    drawStart() {
        this.drawing = true;
        // 添加第一个顶点
        // todo sequence
        const vertexId = this.featureOption.addVertex!(this.latLngs[0][0], [this], { className: 'vertex', size: 4 });
        this.relateNodeIds[0].push(vertexId);
    }

    drawMove(latLng: LatLng) {
        this.dashedLatlngs[0][1] = latLng;
        this.redrawDashedLine();
        if (this.dashedVertex) {
            this.dashedVertex.changeLatlng(latLng, [0]);
        } else {
            const judgeRepeat = false;
            const vertexId = this.featureOption.addVertex!(
                this.dashedLatlngs[0][1],
                [],
                {
                    className: 'vertex dashed-vertex',
                    size: 4,
                },
                judgeRepeat
            );
            this.dashedVertex = <Vertex>this.featureOption.nodesLayer?.features.get(vertexId);
        }
    }

    drawEnd() {
        // 少于三个点时,不做反应
        // todo sequence
        if (this.latLngs[0].length < 3) {
            // this.featureOption.remove!(this);
            return;
        }

        // 首尾相连
        const latLng = this.latLngs[0][0];
        this.latLngs[0].push(latLng);
        this.redraw();

        const { nodesLayer } = this.featureOption;
        if (!nodesLayer) {
            return;
        }

        nodesLayer.remove(this.dashedVertex!.id);
        this.dashedVertex = undefined;
        this.dashedLatlngs = [];
        this.drawing = false;
        this.featureOption.drawEnd!(this);
    }

    changeLatlng(latLng: LatLng, indexs: number[], sequenceNum = 0): void {
        this.dashedLatlngs = [];
        indexs.forEach((item: number, i: number) => {
            if (item === -1) {
                return;
            }
            this.latLngs[sequenceNum].splice(item, 1, latLng);

            if (item === 0) {
                // 因为面是首尾相连，改变第一个坐标，也要同时改变最后一个坐标
                this.latLngs[sequenceNum].splice(this.latLngs[sequenceNum].length - 1, 1, latLng);
                // 拖动顶点改变经纬度时，如果拖动的是第一个顶点，虚线坐标是第一个，第二个和倒数第一个
                this.dashedLatlngs[i] = [this.latLngs[sequenceNum][this.latLngs[sequenceNum].length - 2]].concat(
                    this.latLngs[sequenceNum].slice(0, 2)
                );
            } else if (item === this.latLngs[sequenceNum].length - 1) {
                this.dashedLatlngs[i] = this.latLngs[sequenceNum].slice(-2);
            } else {
                this.dashedLatlngs[i] = this.latLngs[sequenceNum].slice(item - 1, item + 2);
            }
            this.redraw();
            this.redrawDashedLine();
        });
    }

    remove(): void {
        this.group.remove();
        this.latLngs = [];
        const { transform, nodesLayer } = this.featureOption;
        if (!nodesLayer) {
            return;
        }
        if (this.drawing) {
            // this.featureOption.drawEnd!(this);
            if (this.dashedVertex) {
                nodesLayer.remove(this.dashedVertex.id);
            }
        }
        if (this.relateNodeIds && this.relateNodeIds.length > 0) {
            this.relateNodeIds.flat().forEach((nodeId: string) => {
                const node = <Vertex>nodesLayer.features.get(nodeId);
                // 如果顶点不存在，或者经纬度已经被清空，直接返回
                if (!node || node.latLngs.length === 0) {
                    return;
                }
                // 顶点存在多个关联要素,不能直接删除顶点
                if (node.relateFeatures.length > 2) {
                    node.relateFeatures = node.relateFeatures.filter((item) => {
                        return item.id !== this.id;
                    });
                    node.redraw();
                } else if (node.relateFeatures.length === 2) {
                    const relateFeature = node.relateFeatures.find((item) => {
                        return item.id !== this.id;
                    });
                    if (!relateFeature) {
                        nodesLayer.remove(nodeId);
                        return;
                    }
                    const { sequence, indexs } = (relateFeature as Line | Polygon).getNodeIndex(nodeId);

                    if (!indexs.length) {
                        nodesLayer.remove(nodeId);
                        return;
                    }
                    const isStraightLine = indexs.every((index) => {
                        // 判断是否直线上的中间点，关联要素两头的顶点不能被其他要素关联删除
                        if (index > 0 && index < relateFeature.latLngs[sequence].length - 1) {
                            const point = transform.project(relateFeature.latLngs[sequence][index]);
                            const prevPoint = transform.project(relateFeature.latLngs[sequence][index - 1]);
                            const nextPoint = transform.project(relateFeature.latLngs[sequence][index + 1]);
                            // 判断顶点和前后两个点是否在一条直线上，如果在，则删除这个顶点
                            const isStraightLine = pointInSegments(new L.Point(point[0], point[1]), [
                                new L.Point(prevPoint[0], prevPoint[1]),
                                new L.Point(nextPoint[0], nextPoint[1]),
                            ]);
                            return isStraightLine ? true : false;
                        } else {
                            return false;
                        }
                    });
                    if (isStraightLine) {
                        nodesLayer.remove(nodeId);
                        return;
                    }

                    node.relateFeatures = node.relateFeatures.filter((item) => {
                        return item.id !== this.id;
                    });
                    node.redraw();
                } else {
                    nodesLayer.remove(nodeId);
                }
            });
        }
    }

    /**
     * 是否是满足形成要素的最小经纬度数量
     * @returns
     */
    isMinLatlngNum() {
        const len = this.latLngs.length;
        return this.latLngs.flat().length === 4 * len;
    }

    /**
     * 根据nodeId获取在relateNodeIds中的位置
     * @param nodeId
     * @returns
     */
    getNodeIndex(nodeId: string) {
        const sequence = this.relateNodeIds.findIndex((item) => item.includes(nodeId));
        const indexs: number[] = [];
        if (sequence > -1) {
            // 自相交的线关联id会重复，需要遍历出来，不能用findIndex
            this.relateNodeIds[sequence]
                .map((id, index) => {
                    return nodeId === id ? index : undefined;
                })
                .forEach((item) => {
                    if (item !== undefined) {
                        indexs.push(item);
                    }
                });
        }

        return { sequence, indexs };
    }

    /**
     * 如果关联顶点被删除一个，并且处于绘制中，虚线需要重新生成
     * todo 层级num
     */
    ajustDashLine() {
        this.drawing = true;
        const lastLatlng = JSON.parse(JSON.stringify(this.latLngs[0][this.latLngs.length - 1]));
        this.dashedLatlngs[0] = [lastLatlng, lastLatlng, this.latLngs[0][0]];
        // this.drawMove(lastLatlng);
    }

    changeMidVertexVisible(visible: boolean) {
        this.relateNodeIds.flat().forEach((nodeId) => {
            this.featureOption.nodesLayer?.changeVertexVisible(nodeId, visible);
        });
    }

    /**
     * 添加线和面上的中间点
     */
    addMidPoint(position: { x: number; y: number }) {
        if (this.type !== 'line' && this.type !== 'polygon') {
            return;
        }

        const { addHistory, getCrossPoint } = this.featureOption;
        if (!getCrossPoint) {
            return;
        }
        // 添加基类
        addHistory(
            {
                annotation: getAnotation('add base '),
                features: [this],
            },
            true
        );

        // 交叉点和feature需要插入的点的index
        const cross = getCrossPoint(this, position);
        if (!cross) {
            return;
        }
        const { latLng, index, sequence } = cross;
        const { addVertex } = this.featureOption;
        const vertexId = addVertex!(latLng, [this], { className: 'vertex', size: 4 });
        this.latLngs[sequence].splice(index, 0, latLng);
        this.relateNodeIds[sequence].splice(index, 0, vertexId);

        addHistory({
            annotation: getAnotation('add midpoint ', this),
            features: [this],
        });
    }

    /**
     * 经纬度转像素点坐标
     * @returns
     */
    toPoints(): Point[][] {
        return this.latLngs.map((latLngItem) => {
            return latLngItem.map((latLng) => {
                const pix = this.featureOption.transform.project(latLng);
                return new Point(pix[0], pix[1]);
            });
        });
    }

    instance() {
        const attr: FeatureInstance = {
            ...this.json(),
            getInstanceType: this.getInstanceType.bind(this),
            changeId: this.changeId.bind(this),
            addMidPoint: this.addMidPoint.bind(this),
            setProperties: this.setProperties.bind(this),
            isEmpty: this.isEmpty.bind(this),
        };
        return deepClone(attr);
    }
}

export default Polygon;
